#pragma once

#include "string.hpp"
#include "sstream.hpp"
#include "vector.hpp"
#include "algorithm.hpp"

#include <locale>

namespace cpac
{
    template< typename _Type >
    _Type from_string(string const& in_text)
    {
        stringstream stream(in_text.c_str());
        _Type out_result;
        stream >> out_result;
        return out_result;
    }

    template < >
    inline bool from_string< bool >(string const& in_text)
    {
        if(in_text == "true")
            return true;
        else return false;
    }

    template< typename _Type >
    string to_string(_Type in_number)
    {
        stringstream stream;
        stream << in_number;
        return stream.str();
    }

    // Tokeniser predicate structures
    struct is_space
        : std::unary_function< char, bool >
    {
        is_space(std::locale const& in_locale = std::locale("english"))
            : _locale(in_locale) {}

        bool operator()(char in_character) const
        {
            return std::isspace(in_character, _locale);
        }

    private:
        std::locale _locale;
    };

    struct is_slash
        : std::unary_function< char, bool >
    {
        bool operator()(char in_character) const
        {
            return (in_character == '/');
        }
    };

    struct is_delimeter
        : std::unary_function< char, bool >
    {
        is_delimeter(char in_delimeter)
            : _delimeter(in_delimeter) {}

        bool operator()(char in_character)
        {
            return (in_character == _delimeter);
        }
    private:
        char _delimeter;
    };

    // The string tokeniser
    template< class _Predicate = is_space >
    class string_tokeniser
    {
    public:
        static void tokenise(string const& in_string, 
                             vector< string >::type& out_strings, 
                             _Predicate const& in_predicate = _Predicate());
    };

    template< class _Predicate >
    inline void string_tokeniser< _Predicate >::tokenise(string const& in_string, vector< string >::type& out_strings, _Predicate const& in_predicate)
    {
        out_strings.clear();

        auto iterator  = cbegin(in_string);
        auto token_end = cbegin(in_string);

        while(iterator != std::end(in_string))
        {
            if(in_predicate(*iterator))
                ++iterator;
            else
            {
                token_end = std::find_if(iterator, std::end(in_string), in_predicate);
                if(iterator < token_end)
                    out_strings.push_back(string(iterator, token_end));
                iterator = token_end;
            }
        }
    }

    inline void trim(string& in_trim, string const& in_delimeters = "\t\r\n")
    {
        in_trim.erase(0, in_trim.find_first_not_of(in_delimeters));
        in_trim.erase(in_trim.find_last_not_of(in_delimeters) + 1);
    }
}
